package core

import (
	"errors"
	"strconv"
	"sync"
	"time"
)

const epoch int64 = 1577808000000

type snowflakeId struct {
	mux sync.Mutex // 互斥锁

	machineIdBits  int64 // 机器ID位数
	sequenceBits   int64 // 序列在id中占的位数
	sequenceMax    int64 // 最大序列值
	sequenceShift  int64 // 序列左移位数
	timestampShift int64 // 时间戳左移位数

	timestamp int64 // 上次时间戳
	machineId int64 // 机器ID
	sequence  int64 // 当前序列
}

var SnowflakeId, _ = NewSnowflakeId(0, 12, 0)

func NewSnowflakeId(machineIdBits, sequenceBits, machineId int64) (*snowflakeId, error) {
	if machineIdBits < 0 || machineIdBits > 10 {
		return nil, errors.New("Length of machine id bit must be between 0 and 10")
	}
	if sequenceBits < 0 || sequenceBits > 12 {
		return nil, errors.New("Length of sequence bit must be between 0 and 10")
	}
	machineIdMax := -1 ^ (-1 << machineIdBits)
	if machineId < 0 || int(machineId) > machineIdMax {
		return nil, errors.New("Length of sequence bit must be between 0 and " + strconv.Itoa(machineIdMax))
	}

	return &snowflakeId{
		machineIdBits:  machineIdBits,
		sequenceBits:   sequenceBits,
		machineId:      machineId,
		sequenceMax:    -1 ^ (-1 << sequenceBits),
		sequenceShift:  machineIdBits,
		timestampShift: machineIdBits + sequenceBits,
	}, nil
}

func (this *snowflakeId) NextId() int64 {
	this.mux.Lock()
	defer this.mux.Unlock()

	now := time.Now().UnixNano() / 1e6
	if this.timestamp == now {
		this.sequence++
		if this.sequence > this.sequenceMax {
			for now <= this.timestamp {
				now = time.Now().UnixNano() / 1e6
			}
		}
	} else {
		this.sequence = 0
		this.timestamp = now
	}
	if this.machineIdBits == 0 {
		return ((this.timestamp - epoch) << this.timestampShift) | (this.sequence << this.sequenceShift)
	} else {
		return ((this.timestamp - epoch) << this.timestampShift) | (this.sequence << this.sequenceShift) | this.machineId
	}
}
